"""
Output formatting utilities for the CLI.

Provides various output formats (text, JSON, YAML, table) and formatting
utilities for displaying analysis results and data structures.
"""

import json
import sys
from typing import Dict, Any, List, Optional, Union
from enum import Enum
from pathlib import Path
from datetime import datetime

from .colors import ColorPrinter, Color


class OutputFormat(Enum):
    """Available output formats."""
    TEXT = "text"
    JSON = "json"
    YAML = "yaml"
    TABLE = "table"


class OutputFormatter:
    """Handles formatting and output of analysis results."""
    
    def __init__(self, format_type: OutputFormat = OutputFormat.TEXT):
        self.format_type = format_type
        self.printer = ColorPrinter()
    
    def set_format(self, format_type: OutputFormat):
        """Set the output format."""
        self.format_type = format_type
    
    def format_analysis_result(self, result: Dict[str, Any]) -> str:
        """Format analysis result according to current format."""
        if self.format_type == OutputFormat.JSON:
            return self._format_json(result)
        elif self.format_type == OutputFormat.YAML:
            return self._format_yaml(result)
        elif self.format_type == OutputFormat.TABLE:
            return self._format_table(result)
        else:
            return self._format_text(result)
    
    def print_analysis_result(self, result: Dict[str, Any]):
        """Print formatted analysis result."""
        if self.format_type == OutputFormat.TEXT:
            self._print_text_result(result)
        else:
            formatted = self.format_analysis_result(result)
            print(formatted)
    
    def _format_json(self, data: Dict[str, Any]) -> str:
        """Format data as JSON."""
        return json.dumps(data, indent=2, default=self._json_serializer)
    
    def _format_yaml(self, data: Dict[str, Any]) -> str:
        """Format data as YAML."""
        try:
            import yaml
            return yaml.dump(data, default_flow_style=False, indent=2)
        except ImportError:
            # Fallback to JSON if PyYAML not available
            self.printer.print_warning("PyYAML not available, falling back to JSON format")
            return self._format_json(data)
    
    def _json_serializer(self, obj):
        """JSON serializer for special objects."""
        if isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, Path):
            return str(obj)
        elif hasattr(obj, 'to_dict'):
            return obj.to_dict()
        else:
            return str(obj)
    
    def _format_table(self, data: Dict[str, Any]) -> str:
        """Format data as table."""
        # Simple table format for flat data
        lines = []
        
        if isinstance(data, dict):
            # Key-value table
            max_key_len = max(len(str(k)) for k in data.keys()) if data else 0
            max_val_len = max(len(str(v)) for v in data.values()) if data else 0
            
            lines.append("┌" + "─" * (max_key_len + 2) + "┬" + "─" * (max_val_len + 2) + "┐")
            lines.append("│ " + "Key".ljust(max_key_len) + " │ " + "Value".ljust(max_val_len) + " │")
            lines.append("├" + "─" * (max_key_len + 2) + "┼" + "─" * (max_val_len + 2) + "┤")
            
            for key, value in data.items():
                lines.append("│ " + str(key).ljust(max_key_len) + " │ " + str(value).ljust(max_val_len) + " │")
            
            lines.append("└" + "─" * (max_key_len + 2) + "┴" + "─" * (max_val_len + 2) + "┘")
        
        return "\n".join(lines)
    
    def _format_text(self, data: Dict[str, Any]) -> str:
        """Format data as human-readable text."""
        lines = []
        
        def format_value(value, indent=0):
            prefix = "  " * indent
            if isinstance(value, dict):
                result = []
                for k, v in value.items():
                    if isinstance(v, (dict, list)):
                        result.append(f"{prefix}{k}:")
                        result.append(format_value(v, indent + 1))
                    else:
                        result.append(f"{prefix}{k}: {v}")
                return "\n".join(result)
            elif isinstance(value, list):
                result = []
                for i, item in enumerate(value):
                    if isinstance(item, (dict, list)):
                        result.append(f"{prefix}[{i}]:")
                        result.append(format_value(item, indent + 1))
                    else:
                        result.append(f"{prefix}- {item}")
                return "\n".join(result)
            else:
                return f"{prefix}{value}"
        
        return format_value(data)
    
    def _print_text_result(self, result: Dict[str, Any]):
        """Print analysis result in formatted text."""
        # Print header
        self.printer.print_header("Analysis Result", level=1)
        
        # Print summary information
        if "summary" in result:
            self.printer.print_header("Summary", level=2)
            summary = result["summary"]
            if isinstance(summary, dict):
                for key, value in summary.items():
                    self.printer.print_key_value(key.replace("_", " ").title(), value)
            else:
                self.printer.print_info(str(summary))
            print()
        
        # Print call tree if present
        if "call_tree" in result:
            self.print_call_tree(result["call_tree"])
        
        # Print other sections
        for key, value in result.items():
            if key in ["summary", "call_tree"]:
                continue
            
            self.printer.print_header(key.replace("_", " ").title(), level=2)
            if isinstance(value, dict):
                self.print_key_value_pairs(value)
            elif isinstance(value, list):
                self.print_list(value)
            else:
                self.printer.print_info(str(value))
            print()
    
    def print_call_tree(self, tree_data: Dict[str, Any], max_depth: Optional[int] = None):
        """Print call tree in a visually appealing format."""
        self.printer.print_header("Function Call Tree", level=2)
        
        if "root_node" not in tree_data:
            self.printer.print_warning("No call tree data available")
            return
        
        root_node = tree_data["root_node"]
        nodes = tree_data.get("nodes", {})
        
        def print_node(node_data, level=0, is_last=True, visited=None):
            if visited is None:
                visited = set()
            
            if max_depth is not None and level > max_depth:
                return
            
            node_id = f"{node_data.get('file_path', '')}:{node_data.get('line_number', 0)}:{node_data.get('name', '')}"
            if node_id in visited:
                self.printer.print_colored("  " * level + "└─ [RECURSIVE]", Color.BRIGHT_RED)
                return
            
            visited.add(node_id)
            
            # Print node information
            name = node_data.get("name", "unknown")
            file_path = node_data.get("file_path", "")
            line_number = node_data.get("line_number", 0)
            complexity = node_data.get("complexity_score", 0)
            
            # Format node display
            node_text = f"{name}"
            if file_path:
                node_text += f" ({Path(file_path).name}:{line_number})"
            if complexity > 0:
                node_text += f" [complexity: {complexity:.1f}]"
            
            # Print with tree structure
            self.printer.print_call_tree_node(node_text, level, is_last, 
                                            node_data.get("callee_count", 0) > 0)
            
            # Print callees (children)
            callee_count = node_data.get("callee_count", 0)
            if callee_count > 0 and level < (max_depth or 10):
                # Find callee nodes
                callees = []
                for node_key, node_info in nodes.items():
                    if node_info.get("name") in [caller.get("name") for caller in node_data.get("callers", [])]:
                        callees.append(node_info)
                
                for i, callee in enumerate(callees):
                    is_last_child = i == len(callees) - 1
                    print_node(callee, level + 1, is_last_child, visited.copy())
        
        print_node(root_node)
        
        # Print tree statistics
        metadata = tree_data.get("metadata", {})
        if metadata:
            print()
            self.printer.print_info(f"Tree Statistics:")
            stats = {
                "Total Nodes": metadata.get("total_nodes", 0),
                "Max Depth": metadata.get("max_depth", 0),
                "Analysis Type": metadata.get("analysis_type", "unknown"),
                "Language": metadata.get("language", "unknown")
            }
            self.print_key_value_pairs(stats, indent=2)
    
    def print_key_value_pairs(self, data: Dict[str, Any], indent: int = 0):
        """Print key-value pairs with formatting."""
        for key, value in data.items():
            key_formatted = key.replace("_", " ").title()
            if isinstance(value, dict):
                self.printer.print_colored("  " * indent + f"{key_formatted}:", Color.BRIGHT_WHITE)
                self.print_key_value_pairs(value, indent + 1)
            elif isinstance(value, list):
                self.printer.print_colored("  " * indent + f"{key_formatted}:", Color.BRIGHT_WHITE)
                self.print_list(value, indent + 1)
            else:
                self.printer.print_key_value(
                    "  " * indent + key_formatted, 
                    str(value),
                    Color.BRIGHT_WHITE,
                    Color.WHITE
                )
    
    def print_list(self, items: List[Any], indent: int = 0):
        """Print list items with formatting."""
        for item in items:
            if isinstance(item, dict):
                if "name" in item:
                    # Likely a node or similar object
                    name = item["name"]
                    details = []
                    if "file_path" in item:
                        details.append(f"file: {Path(item['file_path']).name}")
                    if "line_number" in item:
                        details.append(f"line: {item['line_number']}")
                    if "complexity_score" in item:
                        details.append(f"complexity: {item['complexity_score']:.1f}")
                    
                    detail_str = f" ({', '.join(details)})" if details else ""
                    self.printer.print_list_item(f"{name}{detail_str}", indent=indent)
                else:
                    # Generic dictionary
                    self.printer.print_list_item(str(item), indent=indent)
            else:
                self.printer.print_list_item(str(item), indent=indent)
    
    def print_error_details(self, error_data: Dict[str, Any]):
        """Print detailed error information."""
        self.printer.print_header("Error Details", level=2)
        
        # Basic error info
        self.printer.print_key_value("Error Code", error_data.get("error_code", "UNKNOWN"))
        self.printer.print_key_value("Message", error_data.get("message", "No message"))
        
        # Context information
        if "context" in error_data:
            self.printer.print_header("Context", level=3)
            self.print_key_value_pairs(error_data["context"], indent=1)
        
        # Error details
        if "details" in error_data:
            self.printer.print_header("Details", level=3)
            details = error_data["details"]
            if isinstance(details, dict):
                self.print_key_value_pairs(details, indent=1)
            else:
                self.printer.print_info(f"  {details}")
        
        # Timestamp
        if "timestamp" in error_data:
            self.printer.print_key_value("Timestamp", error_data["timestamp"])
    
    def print_progress_update(self, message: str, current: int, total: int):
        """Print progress update with percentage."""
        percentage = (current / total) * 100 if total > 0 else 0
        progress_bar = self._create_progress_bar(percentage)
        self.printer.print_progress(f"{message} {progress_bar} {current}/{total} ({percentage:.1f}%)")
    
    def _create_progress_bar(self, percentage: float, width: int = 30) -> str:
        """Create ASCII progress bar."""
        filled = int((percentage / 100) * width)
        bar = "█" * filled + "░" * (width - filled)
        return f"[{bar}]"
    
    def print_file_tree(self, files: List[str], title: str = "Files"):
        """Print file tree structure."""
        self.printer.print_header(title, level=2)
        
        # Group files by directory
        dirs = {}
        for file_path in files:
            path = Path(file_path)
            dir_name = str(path.parent)
            if dir_name not in dirs:
                dirs[dir_name] = []
            dirs[dir_name].append(path.name)
        
        # Print directory tree
        for i, (dir_name, file_names) in enumerate(dirs.items()):
            is_last_dir = i == len(dirs) - 1
            
            # Print directory
            self.printer.print_colored(f"📁 {dir_name}/", Color.BRIGHT_BLUE)
            
            # Print files in directory
            for j, file_name in enumerate(sorted(file_names)):
                is_last_file = j == len(file_names) - 1
                prefix = "└── " if is_last_file else "├── "
                self.printer.print_colored(f"  {prefix}📄 {file_name}", Color.WHITE)
    
    def print_session_info(self, session_data: Dict[str, Any]):
        """Print session information."""
        self.printer.print_header("Session Information", level=2)
        
        # Basic session info
        basic_info = {
            "Session ID": session_data.get("session_id", "unknown"),
            "Created": session_data.get("created_at", "unknown"),
            "Last Modified": session_data.get("last_modified", "unknown"),
            "Analysis Count": len(session_data.get("analyses", [])),
            "Total Nodes": session_data.get("total_nodes", 0)
        }
        
        self.print_key_value_pairs(basic_info)
        
        # Recent analyses
        analyses = session_data.get("analyses", [])
        if analyses:
            print()
            self.printer.print_header("Recent Analyses", level=3)
            for analysis in analyses[-5:]:  # Show last 5
                file_path = analysis.get("file_path", "unknown")
                function_name = analysis.get("function_name", "unknown")
                timestamp = analysis.get("timestamp", "unknown")
                self.printer.print_list_item(f"{Path(file_path).name}::{function_name} ({timestamp})")
    
    def save_output_to_file(self, data: Dict[str, Any], file_path: str):
        """Save formatted output to file."""
        path = Path(file_path)
        
        # Determine format from file extension
        if path.suffix.lower() == '.json':
            content = self._format_json(data)
        elif path.suffix.lower() in ['.yml', '.yaml']:
            content = self._format_yaml(data)
        else:
            content = self._format_text(data)
        
        # Write to file
        try:
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            self.printer.print_success(f"Output saved to {path}")
        except Exception as e:
            self.printer.print_error(f"Failed to save output: {e}")